<?php

declare(strict_types=1);

namespace App\Controller\Webhook;

use App\Repository\WebhookRepository;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\Http\Header;
use Yiisoft\Http\Status;
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Router\UrlGeneratorInterface;

use function is_array;
use function is_numeric;
use function is_string;
use function trim;

final readonly class UpdateResponseAction
{
    public function __construct(
        private WebhookRepository $webhooks,
        private ResponseFactoryInterface $responseFactory,
        private UrlGeneratorInterface $urlGenerator,
        private CurrentRoute $currentRoute,
    ) {}

    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        $token = $this->currentRoute->getArgument('token');

        if ($token === null) {
            return $this->responseFactory->createResponse(Status::NOT_FOUND);
        }

        $webhook = $this->webhooks->findByToken($token);
        if ($webhook === null) {
            return $this->responseFactory->createResponse(Status::NOT_FOUND);
        }

        $parsedBody = $request->getParsedBody();
        $status = $this->extractStatus($parsedBody, $webhook['response_status']);
        $contentType = $this->extractContentType($parsedBody, $webhook['response_content_type']);
        $headers = $this->extractHeaders($parsedBody);
        $bodyContent = $this->extractBody($parsedBody);

        $this->webhooks->updateResponse(
            $webhook['id'],
            $status,
            $contentType,
            $headers,
            $bodyContent,
        );

        $response = $this->responseFactory->createResponse(Status::SEE_OTHER);

        return $response->withHeader(
            Header::LOCATION,
            $this->urlGenerator->generate('webhook.show', ['token' => $token]),
        );
    }

    private function extractStatus(mixed $parsedBody, int $fallback): int
    {
        if (!is_array($parsedBody)) {
            return $fallback;
        }

        $value = $parsedBody['status'] ?? null;
        if (!is_numeric($value)) {
            return $fallback;
        }

        $status = (int) $value;

        if ($status < 100 || $status > 599) {
            return $fallback;
        }

        return $status;
    }

    private function extractContentType(mixed $parsedBody, string $fallback): string
    {
        if (!is_array($parsedBody)) {
            return $fallback;
        }

        $value = $parsedBody['content_type'] ?? null;
        if (!is_string($value)) {
            return $fallback;
        }

        $value = trim($value);

        return $value === '' ? $fallback : $value;
    }

    private function extractHeaders(mixed $parsedBody): string
    {
        if (!is_array($parsedBody)) {
            return '';
        }

        $value = $parsedBody['headers'] ?? '';
        if (!is_string($value)) {
            return '';
        }

        return trim($value);
    }

    private function extractBody(mixed $parsedBody): string
    {
        if (!is_array($parsedBody)) {
            return '';
        }

        $value = $parsedBody['body'] ?? '';
        if (!is_string($value)) {
            return '';
        }

        return $value;
    }
}
